﻿using System.Collections.Concurrent;
using System.Linq.Expressions;
using Dyao.Repository.Abstractions;

namespace Dyao.Book.Repository
{
    using Dyao.Book.Contract;
    using Dyao.Book.Entity;
    using Foundation.Replacement;

    public class TaskCenterRecordExtensionRepository : DataRepository<TaskCenterRecordExtensionEntity, int>, ITaskCenterRecordExtensionRepository
    {
        private static ConcurrentDictionary<(Type, string), object> _modelMappingSelectDict = new ConcurrentDictionary<(Type, string), dynamic>();

        /// <summary>
        /// 获取任务记录
        /// </summary>
        /// <param name="id">id</param>
        /// <param name="cancellationToken">cancellationToken</param>
        /// <returns></returns>
        public async Task<TaskCenterRecordInfoDto> GetByIdAsync(int id, CancellationToken cancellationToken = default)
        {
            var taskCenterRecord = await FindAsync(id, cancellationToken);
            TaskCenterRecordInfoDto taskCenterRecordInfoModel = new TaskCenterRecordInfoDto
            {
                Id = taskCenterRecord.ID,
                ExcutingInfo = taskCenterRecord.ExcutingInfo,
                ExcuteResult = taskCenterRecord.ExcuteResult,
                Params = taskCenterRecord.Params,
            };
            return taskCenterRecordInfoModel;
        }

        /// <summary>
        /// 根据属性名获取任务扩展信息
        /// </summary>
        /// <param name="id">id</param>
        /// <param name="propertyNames">属性名</param>
        /// <param name="cancellationToken">任务令牌</param>
        /// <returns></returns>
        public async Task<TaskCenterRecordInfoDto> GetByPropertyNameAsync(int id, string[] propertyNames, CancellationToken cancellationToken = default)
        {
            var selector = GetOrCreateSeletor<TaskCenterRecordInfoDto>(propertyNames);

            var query = GetQueryable().Where(p => p.ID == id).Select(selector);

            return await query.FirstOrDefaultAsync(cancellationToken);
        }

        /// <summary>
        /// 根据属性名更新属性
        /// </summary>
        /// <param name="extension">任务扩展信息</param>
        /// <param name="propertyNames">属性名</param>
        /// <param name="cancellationToken">取消令牌</param>
        /// <returns></returns>
        public Task UpdateByPropertyNameAsync(TaskCenterRecordExtensionEntity extension, string[] propertyNames, CancellationToken cancellationToken = default)
        {
            cancellationToken.ThrowIfCancellationRequested();

            if (extension.ID > 0)
            {
                var entry = DbSet.Attach(extension);
                foreach (var p in propertyNames)
                {
                    var property = entry.Property(p);
                    if (property != null)
                    {
                        property.IsModified = true;
                    }
                }
            }

            return Task.CompletedTask;
        }


        private static Expression<Func<TaskCenterRecordExtensionEntity, T>> GetOrCreateSeletor<T>(string[] propertyNames)
            where T : class, new()
        {
            var list = propertyNames.Select(a => a.ToLower()).ToList();
            list.Sort();

            var targetType = typeof(T);
            var selector = _modelMappingSelectDict.GetOrAdd((targetType, list.JoinAsString("|")), (key) =>
             {
                 var sourceParameter = Expression.Parameter(typeof(TaskCenterRecordExtensionEntity), "p");

                 var sourceType = typeof(TaskCenterRecordExtensionEntity);
                 var targetProperties = targetType.GetProperties();
                 var memberBindingList = new List<MemberBinding>();

                 foreach (var property in targetProperties)
                 {
                     if (list.Contains(property.Name.ToLower()))
                     {
                         var sourceProperty = sourceType.GetProperty(property.Name);
                         if (sourceProperty != null)
                         {
                             //todo:property type check
                             var memberPropertyExpr = Expression.Property(sourceParameter, sourceProperty);
                             var memberBinding = Expression.Bind(property, memberPropertyExpr);
                             memberBindingList.Add(memberBinding);
                         }
                     }
                 }

                 var memberInitExpr = Expression.MemberInit(Expression.New(targetType), memberBindingList.ToArray());
                 var lambda = Expression.Lambda<Func<TaskCenterRecordExtensionEntity, T>>(memberInitExpr, sourceParameter);
                 return lambda;
             });

            return (Expression<Func<TaskCenterRecordExtensionEntity, T>>)selector;

        }
    }
}
